import { type ChangeObject, diffLines } from "diff";
import pc from "picocolors";
import utils from "./utils.js";

export interface DiffLine {
  type: "added" | "removed" | "unchanged";
  content: string;
  count: number;
}

type DiffLineLib = ChangeObject<string>;

function computeDiff(oldContent: string, newContent: string): DiffLine[] {
  const oldLines = oldContent.split("\n");
  const newLines = newContent.split("\n");
  const diff: DiffLine[] = [];

  let i = 0;
  let j = 0;

  while (i < oldLines.length || j < newLines.length) {
    if (
      i < oldLines.length &&
      j < newLines.length &&
      oldLines[i] === newLines[j]
    ) {
      // Lines are the same
      diff.push({ type: "unchanged", content: oldLines[i], count: 1 });
      i++;
      j++;
    } else if (
      j < newLines.length &&
      (i >= oldLines.length || oldLines[i] !== newLines[j])
    ) {
      // Line added
      diff.push({ type: "added", content: newLines[j], count: 1 });
      j++;
    } else if (
      i < oldLines.length &&
      (j >= newLines.length || oldLines[i] !== newLines[j])
    ) {
      // Line removed
      diff.push({ type: "removed", content: oldLines[i], count: 1 });
      i++;
    }
  }

  return diff;
}

/**
 *
 * @param diff
 */
function displayDiff(diff: DiffLine[]): void {
  console.log("\n📊 File changes:");
  console.log(utils.bar);

  for (let i = 0; i < diff.length; i++) {
    const line = diff[i];
    switch (line.type) {
      case "added":
        logAdded(line.content, String(i + 1));
        break;
      case "removed":
        logRemoved(line.content, String(i + 1));
        break;
      case "unchanged":
        logUnchanged(line.content, String(i + 1));
        break;
    }
  }

  console.log(utils.bar);
}

function computeDiffLib(oldContent: string, newContent: string) {
  return diffLines(oldContent, newContent);
}

function displayDiffLib(diff: ChangeObject<string>[]): void {
  console.log("\n📊 File changes:");
  console.log(utils.bar);

  let recordLineCount = 0;
  for (let i = 0; i < diff.length; i++) {
    const line = diff[i];

    switch (true) {
      case line.added:
        if (line.value.includes("\n")) {
          const subLine = line.value.split("\n");
          subLine.pop();
          recordLineCount += subLine.length;
          let j = 0;
          for (const subLineItem of subLine) {
            logAdded(subLineItem, String(i + j + 1));
            j++;
          }
        }
        break;
      case line.removed:
        if (line.value.includes("\n")) {
          const subLine = line.value.split("\n");
          subLine.pop();
          recordLineCount += subLine.length;
          let j = 0;
          for (const subLineItem of subLine) {
            logRemoved(subLineItem, String(i + j + 1));
            j++;
          }
        }
        break;
      default:
        if (line.value.includes("\n")) {
          const subLine = line.value.split("\n");
          subLine.pop();
          recordLineCount += subLine.length;
          let j = 0;
          for (const subLineItem of subLine) {
            logUnchanged(subLineItem, String(i + j + 1));
            j++;
          }
        }
        break;
    }
  }

  console.log(utils.bar);
}

function logAdded(content: string, lineData: string): void {
  console.log(pc.bgGreen(pc.black(`${`+${lineData}`.padStart(4)}|${content}`)));
}
function logRemoved(content: string, lineData: string): void {
  console.log(pc.bgRed(pc.white(`${`-${lineData}`.padStart(4)}|${content}`)));
}
function logUnchanged(content: string, lineData: string): void {
  console.log(`${lineData.padStart(4)}|${content}`);
}

export { computeDiff, computeDiffLib, displayDiff, displayDiffLib };
